package engine;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingWorker;
import treetagger.Normalizer;
import ui.UserInterface;
import utilities.FileTools;
import utilities.ReaderTools;
import utilities.WriterTools;

public class IndexMakerOptimized extends SwingWorker<Void, Integer> {

    private int countDoc = 0;
    private int countUniqueWords = 0;
    private ArrayList<File> foundFiles;
    private ArrayList<String> linesIndex;
    private TreeMap<String, String> linesIndexTree;
    private HashMap<String, Integer> tfMap;//Terme Frequency
    private String corpusPath;
    private Normalizer normalizer;
    private UserInterface guiToUpdate;
    private String indexFileName;
    private IndexMaker.IndexType indexType;

    public IndexMakerOptimized(String corpusPath,
            Normalizer normalizer,
            String indexFileName,
            IndexMaker.IndexType indexType) {

        this.corpusPath = corpusPath;
        this.normalizer = normalizer;
        this.indexFileName = indexFileName;
        this.indexType = indexType;

        foundFiles = new ArrayList<File>();
        tfMap = new HashMap<String, Integer>();
        linesIndex = new ArrayList<String>();
        linesIndexTree = new TreeMap<String, String>();
    }

    public void setGuiToUpdate(UserInterface guiToUpdate) {
        this.guiToUpdate = guiToUpdate;
    }

    public static boolean wordFilter(String word) {
        boolean noError = true;
        if (word.charAt(0) < 97 || word.charAt(0) > 122) {
            noError = false;
        }
        return noError;
    }

    public void generateTF(String filePath) {
        tfMap.clear();
        int nbOccurencesMotCourant;

        for (String mot : this.normalizer.normalizeFromFile(filePath, Normalizer.SW_PATH)) {
            if (wordFilter(mot) == true) {
                if (tfMap.containsKey(mot)) {
                    nbOccurencesMotCourant = tfMap.get(mot);
                } else {
                    nbOccurencesMotCourant = 0;
                }
                tfMap.put(mot, nbOccurencesMotCourant + 1);
            }
        }

        writeTFDocIndex(countDoc);
        countDoc++;
        //System.out.println("TF Progress: " + countDoc + " / " + foundFiles.size());
    }

    public void writeDocPath() {
        String string = "";
        int count = 0;
        for (File file : foundFiles) {
            string += "D" + count + ";" + file.getAbsolutePath() + "\n";
            count++;
            System.out.println("DocPath Progress: " + count + " / " + foundFiles.size());
        }
        try {
            WriterTools.write("documentPathMapping.txt", string);
        } catch (IOException ex) {
            Logger.getLogger(IndexMaker.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void writeIndex() {
        //modifier lineIndex


        linesIndex.add("nbDoc:0");
        linesIndex.add("nbWord:0");

        for (int i = 97; i < 123; i++) {
            String string = (char) i + ":-1:-1";
            linesIndex.add(string);
        }

        try {
            String string = "";
            for (String line : linesIndex) {
                string += line + "\n";
            }
            //System.out.println("indexFileName: " + indexFileName);
            WriterTools.write(indexFileName, string);
        } catch (IOException ex) {
            Logger.getLogger(IndexMaker.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void writeFinal() {
        //Suppression des mots qui n'apparraissent que dans un seul document
        //Set<Entry<String,String>> entryMap = linesIndexTree.entrySet();
        TreeMap<String, String> copy = (TreeMap<String, String>) linesIndexTree.clone();

        //for(Map.Entry<String,String> e : entryMap){
        for (Map.Entry<String, String> e : copy.entrySet()) {
            if ((e.getValue().split("D").length) < 3) {
                linesIndexTree.remove(e.getKey());  
            }
        }


        try {
            String string = "";

            string += "nbDoc:" + this.countDoc + "\n";
            string += "nbWord:" + linesIndexTree.size();



            TreeMap<String, ArrayList<Integer>> sommary = new TreeMap<String, ArrayList<Integer>>();
            String currentChar = "?";
            int currentCharStart = 27;
            int currentCharEnd = 27;

            for (Map.Entry<String, String> elt : linesIndexTree.entrySet()) {

                if ((elt.getKey().charAt(0)) == currentChar.charAt(0)) {
                    currentCharEnd++;
                } else {
                    ArrayList<Integer> interval = new ArrayList<Integer>();
                    interval.add(currentCharStart);
                    interval.add(currentCharEnd);
                    if (currentChar != "?") {
                        sommary.put("" + currentChar, interval);
                    }
                    currentCharEnd++;
                    currentCharStart = currentCharEnd;
                    currentChar = "" + elt.getKey().charAt(0);
                }
                //string += "\n"+elt.getKey()+elt.getValue();
            }
            if (currentChar != "?") {
                ArrayList<Integer> interval = new ArrayList<Integer>();
                interval.add(currentCharStart);
                interval.add(currentCharEnd);
                sommary.put(currentChar, interval);
            }

            /*for(Map.Entry<String, ArrayList<Integer>> elt : sommary.entrySet())
             {
             string += "\n"+elt.getKey()+":"+elt.getValue().get(0)+":"+elt.getValue().get(1);
             }*/

            for (int i = 97; i < 123; i++) {
                String letter = "" + (char) i;
                if (sommary.containsKey(letter)) {

                    string += "\n" + letter + ":" + sommary.get(letter).get(0) + ":" + sommary.get(letter).get(1);
                } else {
                    string += "\n" + letter + ":-1:-1";
                }
            }


            for (Map.Entry<String, String> elt : linesIndexTree.entrySet()) {
                string += "\n" + elt.getKey() + elt.getValue();
            }


            WriterTools.write(indexFileName, string);
        } catch (IOException ex) {
            Logger.getLogger(IndexMaker.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void writeTFDocIndex(int fileID) {
        // TODO 
        // ReaderTools.readFileIndexSN(linesIndexSN); // MAJ linesIndexSN

        String wordCurrent = "";
        int countMax = 0;
        for (Map.Entry<String, Integer> entry : tfMap.entrySet()) {
            wordCurrent = entry.getKey();


            if (linesIndexTree.containsKey(entry.getKey())) {
                String string = linesIndexTree.get(entry.getKey()) + "D" + fileID + ";" + entry.getValue() + ",";
                linesIndexTree.put(entry.getKey(), string);
            } else {
                linesIndexTree.put(entry.getKey(), ":D" + fileID + ";" + entry.getValue() + ",");
                countUniqueWords++;
            }


            // getWordFromIndex(wordCurrent, entry.getValue(), fileID);
            countMax++;
        }


    }

    public void incStartEndSomary(int numberLetter, int numberLine) {
        String wordLine = linesIndex.get(numberLetter);
        String[] wordLineElement = new String[3];
        wordLineElement = wordLine.split(":");
        if (wordLineElement[1].compareTo("-1") == 0) {
            wordLineElement[1] = "" + numberLine;
            wordLineElement[2] = "" + numberLine;
        } else {
            wordLineElement[2] = "" + ((Integer.parseInt(wordLineElement[2])) + 1);
        }
        linesIndex.set(numberLetter, wordLineElement[0] + ":" + wordLineElement[1] + ":" + wordLineElement[2]);
        for (int i = numberLetter + 1; i < 28; i++) {
            String line = linesIndex.get(i);
            String[] lineElement = new String[3];
            lineElement = line.split(":");

            if (lineElement[1].compareTo("-1") != 0) {
                lineElement[1] = "" + ((Integer.parseInt(lineElement[1])) + 1);
                lineElement[2] = "" + ((Integer.parseInt(lineElement[2])) + 1);
                linesIndex.set(i, lineElement[0] + ":" + lineElement[1] + ":" + lineElement[2]);
            }
        }
    }

    public int jump(String[] lineElement, String word) {

        int rootSquare = (int) Math.sqrt((double) Integer.parseInt(lineElement[2]) - (double) Integer.parseInt(lineElement[1]));
        int end = (int) Integer.parseInt(lineElement[2]);
        int start = (int) Integer.parseInt(lineElement[1]);
        if (rootSquare != 0) {
            do {
                String nextLine = linesIndex.get(start);
                String[] nextLineElement = new String[2];
                nextLineElement = nextLine.split(":");
                if (nextLineElement[0].compareTo(word) == 0) {
                    break;
                } else if (nextLineElement[0].compareTo(word) > 0) {
                    if (start != (int) Integer.parseInt(lineElement[1])) {
                        start -= rootSquare;
                    }
                    break;
                } else if (nextLineElement[0].compareTo(word) < 0) {
                    start += rootSquare;
                }

            } while (start < end);
            if (start >= end) {
                start -= rootSquare;
            }
        }

        return start;
    }

    public void getWordFromIndex(String word, int wordCount, int fileID) {

        boolean found = false;
        char firstLetter = word.charAt(0);

        int numberLetter = ((int) firstLetter) - 95;
        int count = 0;
        String line = linesIndex.get(numberLetter);
        String[] lineElement = new String[3];
        lineElement = line.split(":"); // 0 -> letter 1 -> start 2 -> end
        // Pas de mot qui commence par cette lettre


        if (lineElement[1].compareTo("-1") == 0) {

            while (numberLetter - count > 1 && found == false) {
                String prevLine = linesIndex.get(numberLetter - count);
                String[] prevLineElement = new String[3];
                prevLineElement = prevLine.split(":");


                // Cas : Pas de mot, on le rajoute à la fin de la dernière lettre
                if (prevLineElement[1].compareTo("-1") != 0) {
                    String string = word + ":D" + fileID + ";" + wordCount + ",";
                    int jump = (Integer.parseInt(prevLineElement[2]) + 1);
                    linesIndex.add(jump, string);
                    incStartEndSomary(numberLetter, jump);
                    this.countUniqueWords++;
                    found = true;
                }
                count++;
            }
            if (found == false) {
                String string = word + ":D" + fileID + ";" + wordCount + ";";
                linesIndex.add(28, string);
                incStartEndSomary(numberLetter, 28);
                this.countUniqueWords++;
            }
        } else {

            int start = Integer.parseInt(lineElement[1]);
            int end = Integer.parseInt(lineElement[2]);

            //int lineJumped = jump(lineElement, word);
            int lineJumped = start;
            do {
                String nextLine = linesIndex.get(lineJumped);
                String[] nextLineElement = new String[2];
                nextLineElement = nextLine.split(":");
                if (nextLineElement[0].compareTo(word) == 0) {
                    String wordLine = linesIndex.get(lineJumped);
                    String newWordLine = wordLine + "D" + fileID + ";" + wordCount + ",";
                    linesIndex.set(lineJumped, newWordLine);
                    found = true;
                } else if (nextLineElement[0].compareTo(word) > 0) {
                    break;
                } else if (nextLineElement[0].compareTo(word) < 0) {
                    lineJumped++;
                }

            } while (lineJumped < end && found == false);

            if (found == false) {
                String string = word + ":D" + fileID + ";" + wordCount + ",";
                linesIndex.add(lineJumped, string);

                incStartEndSomary(numberLetter, lineJumped);
                this.countUniqueWords++;
            }
        }
    }

    @Override
    protected Void doInBackground() throws Exception {

        int progress;
        int cpt = 0;

        publish(1);//1%

        FileTools.findFiles(this.corpusPath, foundFiles);
        for (File f : foundFiles) {
            generateTF(f.getAbsolutePath());
            cpt++;


            progress = (int) ((double) cpt * 100 / (double) this.foundFiles.size());
            publish(progress);
        }

        return null;
    }

    @Override
    protected void process(List<Integer> values) {
        for (Integer val : values) {
            if (this.indexType == IndexMaker.IndexType.Tokens) {
                guiToUpdate.updateBarIndexTokens(val);
            } else if (this.indexType == IndexMaker.IndexType.Stem) {
                guiToUpdate.updateBarIndexStem(val);
            }

        }
    }

    @Override
    protected void done() {
        writeFinal();
        writeDocPath();
        //guiToUpdate.indexIsDone();
    }
}
